package fileHandling;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Callable;

import logic.common.team.Team;
import logic.nodes.nodeSettings.Settings;
import map.Map;
import map.SpectatorPoint;
import map.asteroids.AsteroidField;
import map.asteroids.AsteroidFieldProperties;
import map.infos.LightMapInfos;
import map.infos.MapDescription;
import map.infos.MapInfos;
import map.infos.TeamInfos;
import map.spawnStation.SpawnPointProperties;
import map.spawnStation.SpawnStation;

import com.jme.math.Vector3f;

/**
 * Provides static methods to find and create {@link Map}s.
 * 
 * @author Wasserleiche
 */
public class MapLoader {
	
	/** Defines the basis scenario-path. */
	public static final String MAPS_PATH = "data/maps/";
	
	private static List<String> mapNames;
	
	/**
	 * Returns all scenario-names in the given scenario-folder.
	 * @param scenarioFolder The name of the folder to search for scenarios. Can either be MATCH_PATH 
	 * or BACKGROUND_PATH.
	 * @return A {@link List} of scenario-names in the given scenario-folder.
	 */
	public static List<String> findMaps() {
		if(mapNames != null) return mapNames;
		
		mapNames = new ArrayList<String>();
		File f = new File(MAPS_PATH);
		File[] fileArray = f.listFiles();
		for(File file : fileArray) {
			if(file.isDirectory() && !file.getPath().contains(".")) {
				mapNames.add(file.getName());
			}
		}
		return mapNames;
	}
	
	public static MapDescription getMapDescription(String mapName) {
		String path = MAPS_PATH + mapName + "/description/";
		File descrFolder = new File(path);
		
		HashMap<String, String> translations = new HashMap<String, String>();
		
		File[] fileArray = descrFolder.listFiles();
		for(File file : fileArray) {
			if(!file.isDirectory() && !file.getName().startsWith(".") && file.getName().endsWith("xml")) {
				Settings settings = SettingsLoader.loadSettings(path + file.getName());
				if(settings != null) {
					String language = file.getName().replace(".xml", "");
					String description = settings.getValueOf(mapName);
					translations.put(language, description);
				}
			}
		}
		
		return new MapDescription(translations);
	}
	
	/**
	 * Creates a {@link Callable} that returns a {@link MapInfos}-Object with the given information.
	 * @param mapPath The path to the scenario to load.
	 * @return A {@link Callable} that returns a new {@link MapInfos}-Object.
	 */
	public static MapInfos getMapInfos(String mapPath) {
		String matchFile = MAPS_PATH + mapPath + "/mapInfos.xml";
		Settings settings = SettingsLoader.loadSettings(matchFile);
		if(settings == null) return null;
		return new MapInfos(settings);
	}
	
	/**
	 * Creates and returns a {@link SpawnPointProperties}-Object belonging to the given {@link SpawnStation}.
	 * @param spawnStationFile The file-name (and path) to the {@link SpawnStation}.
	 * @return A {@link SpawnPointProperties}-Object that has the information about the given {@link SpawnStation}.
	 */
	public static SpawnPointProperties getSpawnStationProps(String spawnStationFile) {
		Settings settings = SettingsLoader.loadSettings(spawnStationFile);
		if(settings == null) return null;
		return new SpawnPointProperties(settings);
	}
	
	/**
	 * Creates a {@link Callable} that returns a {@link List} of {@link SpawnPointProperties} with the 
	 * given information.
	 * @param mapPath The path to the scenario to load.
	 * @return A {@link Callable} that returns a {@link List} of {@link SpawnPointProperties}.
	 */
	public static List<SpawnPointProperties> getSpawnPointsInfos(String mapPath) {
		String spawnStationPath = MAPS_PATH + mapPath + "/spawnPoints/";
		List<SpawnPointProperties> spawnStations = new ArrayList<SpawnPointProperties>();
		
		File f = new File(spawnStationPath);
		File[] fileArray = f.listFiles();
		if(fileArray == null) return null;
		
		for(int i = 0; i < fileArray.length; i++) {
			if(fileArray[i].isFile()) {
				String spawnStationFileName = spawnStationPath + fileArray[i].getName();
				SpawnPointProperties props = getSpawnStationProps(spawnStationFileName);
				if(props == null) return null;
				spawnStations.add(props);
			}
		}
		return spawnStations;
	}
	
	/**
	 * Creates a {@link Callable} that returns a {@link TeamInfos}-Object with the given information.
	 * @param mapName The path to the scenario-files.
	 * @param team The {@link Team}. Can either be 1 or 2.
	 * @return A {@link Callable} that returns a new {@link TeamInfos}-Object.
	 */
	public static TeamInfos getTeamInfos(String mapName, int team) {
		String scenarioFile = MAPS_PATH + mapName + "/team" + team + ".xml";
		Settings settings = SettingsLoader.loadSettings(scenarioFile);
		if(settings == null) return null;
		return new TeamInfos(settings);
	}
	
	/**
	 * Creates a {@link Callable} that returns a {@link List} of {@link Vector3f} that are positions 
	 * of spectator-points.
	 * @param mapPath The path to the scenario-files.
	 * @param basePath The base-path of the scenario. Can either be MATCH_PATH or BACKGROUND_PATH.
	 * @return A {@link Callable} that returns a {@link List} of {@link Vector3f}.
	 */
	public static List<SpectatorPoint> getSpectatorPoints(String mapPath) {
		BufferedReader reader = null;
		try {
			String scenarioFile = MAPS_PATH + mapPath + "/spectatorPoints.txt";
			reader = new BufferedReader(new FileReader(scenarioFile));
			
			List<SpectatorPoint> camSpawns = new ArrayList<SpectatorPoint>();
			while(reader.ready()) {
				String newLine = reader.readLine();
				camSpawns.add(new SpectatorPoint(newLine));
			}
			return camSpawns;
		} catch(Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			if(reader != null) {
				try { reader.close(); }
				catch(Exception e) { e.printStackTrace(); }
			}
		}
	}
	
	/**
	 * Returns a new {@link AsteroidFieldProperties}-Object from the given asteroidFile.
	 * @param asteroidFile The file that contains the information about the {@link AsteroidField}.
	 * @return A {@link AsteroidFieldProperties}-Object if the file can be read. null, if any error occurres.
	 */
	public static AsteroidFieldProperties getAsteroidField(String asteroidFile) {
		Settings settings = SettingsLoader.loadSettings(asteroidFile);
		if(settings == null) return null;
		return new AsteroidFieldProperties(settings);
	}
	
	/**
	 * Creates a {@link Callable} that returns a {@link List} of {@link AsteroidFieldProperties} of the 
	 * given scenario.
	 * @param mapPath The path to the scenario-files.
	 * @param basePath The base-path of the scenario. Can either be MATCH_PATH or BACKGROUND_PATH.
	 * @return A {@link Callable} that returns a {@link List} of {@link AsteroidFieldProperties}.
	 */
	public static List<AsteroidFieldProperties> getAsteroidFields(String mapPath) {
		String fieldPath = MAPS_PATH + mapPath + "/asteroidFields/";
		List<AsteroidFieldProperties> asteroidFields = new ArrayList<AsteroidFieldProperties>();
		
		File f = new File(fieldPath);
		if(f.listFiles() == null) return null;
		
		for(File subDir : f.listFiles()) {
			if(subDir.getName().startsWith(".")) continue;
			String asteroidPath = fieldPath + subDir.getName() + "/properties.xml";
			AsteroidFieldProperties props = getAsteroidField(asteroidPath);
			if(props == null) return null;
			asteroidFields.add(props);
		}
		return asteroidFields;
	}
	
	public static LightMapInfos getLightMapInfos(String mapName) {
		String matchFile = MAPS_PATH + mapName + "/lightMapInfos.xml";
		Settings settings = SettingsLoader.loadSettings(matchFile);
		if(settings == null) return null;
		return new LightMapInfos(settings);
	}
	
	/**
	 * Creates a new {@link Map} from the given map-path.
	 * @param mapName The name to the map.
	 * @return A new {@link Map} if all files are read correctly.<br>
	 * null, else.
	 */
	public static Map createMap(String mapName) {
		MapInfos matchInfos = getMapInfos(mapName);
		TeamInfos teamAInfos = getTeamInfos(mapName, 1);
		TeamInfos teamBInfos = getTeamInfos(mapName, 2);
		List<SpawnPointProperties> spawnProps = getSpawnPointsInfos(mapName);
		List<SpectatorPoint> camSpawns = getSpectatorPoints(mapName);
		List<AsteroidFieldProperties> asteroidFields = getAsteroidFields(mapName);
		MapDescription description = getMapDescription(mapName);
		
		if(matchInfos == null || teamAInfos == null || teamBInfos == null 
		|| spawnProps == null || camSpawns == null || asteroidFields == null || description == null) 
			return null;
		
		return new Map(matchInfos, teamAInfos, teamBInfos, spawnProps, 
				camSpawns, asteroidFields, description, mapName);
	}
}